package com.bnz.user.service.impl;

import com.alibaba.csp.sentinel.AsyncEntry;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphO;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.bnz.user.service.SentinelService;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Service
public class SentinelServiceImpl implements SentinelService {

    //1. 定义资源的方式一
    @Override
    public String res1() {
        // 1.5.0 版本开始可以利用 try-with-resources 特性（使用有限制）
        // 资源名可使用任意有业务语义的字符串，比如方法名、接口名或其它可唯一标识的字符串。
        try (Entry entry = SphU.entry("res1")) {
            // 被保护的业务逻辑
            System.out.println("受保护资源res1。。。");

        } catch (BlockException ex) {
            // 资源访问阻止，被限流或被降级
            ex.printStackTrace();
            String message = "异常名字：" + ex;
            return message;
        }
        return "success";
    }

    //2. 定义资源方式二：
    @Override
    public String res2() {
        // 资源名可使用任意有业务语义的字符串
        if (SphO.entry("res2")) {
            // 务必保证finally会被执行
            try {
                /**
                 * 被保护的业务逻辑
                 */
                System.out.println("受保护资源res2。。。");
            } finally {
                SphO.exit();
            }
        } else {
            // 资源访问阻止，被限流或被降级
            return "资源res2被阻止，被限流或被降级。。。 ";
        }
        return "success";
    }

    //3. 异步回调方式 (第三种方式)
    @Override
    public String res3() {
        try {
            AsyncEntry entry = SphU.asyncEntry("res3");
            // 异步调用.
            CompletableFuture.runAsync(() -> {
                ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                        // Normal entry nested in asynchronous entry.
                        System.out.println("受保护资源res3。。。");
                    } catch (InterruptedException e) {
                        // Ignore.
                    } finally {
                        entry.exit();
                    }
                });
            });
        } catch (BlockException ex) {
            return "ex: 资源res3进行了熔断、限流或降级处理。。。";
        }
        return "success";
    }


    //4. 处理熔断、限流或降级方法四
    @Override
    //处理BlockException异常方法一：
    // blockHandler 函数访问范围需要是 public，返回类型需要与原方法相匹配，
    // 参数类型需要和原方法相匹配并且最后加一个额外的参数，类型为 BlockException。
    // blockHandler 函数默认需要和原方法在同一个类中。
    // @SentinelResource(value="res4",blockHandler = "handlerRes4")

    //处理BlockException异常方法二：
    // 若希望使用其他类的函数，则可以指定 blockHandlerClass 为对应的类的 Class 对象，并且还需要使用blockHandler属性来指定方法名
    // 注意对应的函数必需为 static 函数，否则无法解析
    // @SentinelResource(value = "res4",blockHandlerClass = HandlerBlockException.class,blockHandler = "handlerRes4Class")

    //处理所有异常方法一：
    // fallback / fallbackClass：fallback 函数名称，可选项，用于在抛出异常的时候提供 fallback 处理逻辑。
    // fallback 函数可以针对所有类型的异常（除了 exceptionsToIgnore 里面排除掉的异常类型）进行处理。
    // fallback 函数签名和位置要求：
    // 返回值类型必须与原函数返回值类型一致；
    // 方法参数列表需要和原函数一致，或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    // fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数，则可以指定 fallbackClass
    // 为对应的类的 Class 对象，注意对应的函数必需为 static 函数，否则无法解析。
    // @SentinelResource(value = "res4",fallback = "handlerResExcept")

    //处理所有异常方法二：
    @SentinelResource(value = "res4",fallback = "handlerResExceptClass",fallbackClass=HandlerAllException.class)
    public String res4() {
        return "success";
    }

    public String handlerRes4(BlockException be){
        return "资源res4进行了熔断、限流或降级处理。。。";
    }
    public String handlerResExcept(Throwable t){
        return "处理任意异常";
    }

}
